Skip to content

fix: preserve binary message bodies when serializing to the wire#126

Open
WaelJami wants to merge 1 commit into
restsend:mainfrom
WaelJami:fix/binary-body-serialization
Open

fix: preserve binary message bodies when serializing to the wire#126
WaelJami wants to merge 1 commit into
restsend:mainfrom
WaelJami:fix/binary-body-serialization

Conversation

@WaelJami

@WaelJami WaelJami commented Jun 8, 2026

Copy link
Copy Markdown

Summary

SIP message bodies are opaque octets (RFC 3261 §7.4), but rsipstack serialized them as UTF-8 text, corrupting any body that isn't valid UTF-8. This makes to_bytes() the canonical wire serializer (preserving the body verbatim) and routes the UDP/TCP/TLS transports through it.

Problem

A message with a binary body, e.g. an application/ISUP part, or an eCall MSD (binary ASN.1 PER), is sent with the body mangled. Every non-UTF-8 byte is replaced by U+FFFD (EF BF BD), which both changes the payload and breaks Content-Length.

Root cause

All message→bytes paths went through Display/to_string(), which renders the body with String::from_utf8_lossy:

// src/sip/message.rs (Display for Request/Response)
write!(f, "...{}\r\n{}", self.headers, String::from_utf8_lossy(&self.body))

and the transports serialized via to_string() (transport/udp.rs, transport/stream.rs).

Notably, the receive path already preserves binary bodies (the parser slices the body by Content-Length), so send and receive were asymmetric.

Fix

  • Add to_bytes() to Request, Response, and SipMessage: start line + headers as ASCII, body appended verbatim.
  • From<…> for Vec<u8> now delegates to to_bytes().
  • UDP send, the TCP/TLS SipCodec encoder, and send_to_stream use to_bytes().

Scope

WebSocket transport is intentionally unchanged: SIP-over-WS (RFC 7118 §5) uses UTF-8 text frames, so binary bodies aren't representable there regardless.

Testing

  • New regression tests: a Request/Response round-trip and the stream codec, each asserting a non-UTF-8 body survives serialization byte-for-byte.
  • Full suite: cargo test --lib → 238 passed, clippy clean.

Compatibility

Backward compatible. Display/to_string() behaviour is unchanged; only the byte serialization used by transports is corrected.

SIP message bodies are opaque octets (RFC 3261 §7.4), but messages were
serialized via Display/to_string(), which renders the body with
String::from_utf8_lossy. Any non-UTF-8 body (e.g. an application/ISUP part
or a binary eCall MSD) was corrupted: each invalid byte became U+FFFD,
which changes the payload and breaks Content-Length.

The receive path already preserves binary bodies (the parser slices by
Content-Length), so send and receive were asymmetric.

Add to_bytes() to Request/Response/SipMessage (start line + headers as
ASCII, body appended verbatim) and route the UDP and TCP/TLS transports
through it. From<_> for Vec<u8> now delegates to to_bytes(). Display is
left lossy for human-readable logging. WebSocket is unchanged: SIP-over-WS
(RFC 7118) uses UTF-8 text frames.

Adds regression tests for Request/Response round-trips and the stream codec.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant